<template>
  <div class="flex flex-col gap-2 relative">
    <div ref="inputRef" class="flex items-center space-x-2">
      <MuiInput v-model="localModelValue"
                   readonly
                   class="text-sm text-gray-500"
                   @click="isOpen = !isOpen"/>
    </div>

    <div v-if="isOpen"
         ref="panelRef"
         class="absolute top-full mt-1 bg-white rounded-sm shadow-lg border w-[530px] border-gray-200 p-2 z-20">
      <div class="flex space-x-2">
        <MuiTab v-model="activeTab" size="small">
          <MuiTabItem v-for="tab in tabs" :label="tab.label" :value="tab.value">
            <MuiSecondPanel v-if="tab.value === 'seconds'" v-model="secondExpression" @update="handleUpdate"/>
            <MuiMinutePanel v-if="tab.value === 'minutes'"
                               v-model="minuteExpression"
                               :cron="cronObject"
                               @update="handleUpdate"/>
            <MuiHourPanel v-if="tab.value === 'hours'"
                             v-model="hourExpression"
                             :cron="cronObject"
                             @update="handleUpdate"/>
            <MuiDayPanel v-if="tab.value === 'day'"
                            v-model="dayExpression"
                            :cron="cronObject"
                            @update="handleUpdate"/>
            <MuiMonthPanel v-if="tab.value === 'month'"
                              v-model="monthExpression"
                              :cron="cronObject"
                              @update="handleUpdate"/>
            <MuiWeekPanel v-if="tab.value === 'week'"
                             v-model="weekExpression"
                             :cron="cronObject"
                             @update="handleUpdate"/>
            <MuiYearPanel v-if="tab.value === 'year'"
                             v-model="yearExpression as any"
                             :cron="cronObject"
                             @update="handleUpdate"/>
          </MuiTabItem>
        </MuiTab>
      </div>

      <div v-if="nextExecutionTimes.length" class="text-gray-500 space-y-1 mt-4 border-t rounded-sm p-2">
        <div class="items-center">{{ t('cron.text.nextExecutionTime') }}:</div>
        <div v-for="(time, index) in nextExecutionTimes" :key="index" class="text-xs">
          {{ t('cron.text.nextExecutionTimeAtOnN', { n: index + 1, time }) }}
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { t } from '@/utils/locale'
import type { CronEmits, CronProps } from './types'
import MuiSecondPanel from './components/MuiSecondPanel.vue'
import MuiMinutePanel from './components/MuiMinutePanel.vue'
import MuiHourPanel from './components/MuiHourPanel.vue'
import MuiDayPanel from './components/MuiDayPanel.vue'
import MuiMonthPanel from './components/MuiMonthPanel.vue'
import MuiWeekPanel from './components/MuiWeekPanel.vue'
import { getRecentTriggerTime } from '@/utils/cron.ts'
import MuiYearPanel from '@/ui/cron/components/MuiYearPanel.vue'

const props = withDefaults(defineProps<CronProps>(), {
  modelValue: '* * * * * ?'
})
const emit = defineEmits<CronEmits>()

const localModelValue = ref(props.modelValue)
const nextExecutionTimes = ref<string[]>([])
const isOpen = ref(false)
const inputRef = ref<HTMLElement | null>(null)
const panelRef = ref<HTMLElement | null>(null)
const activeTab = ref('seconds')

const expressions = computed(() => {
  const parts = (localModelValue.value || '* * * * * ?').trim().split(' ')
  return {
    second: parts[0] || '*',
    minute: parts[1] || '*',
    hour: parts[2] || '*',
    day: parts[3] || '*',
    month: parts[4] || '*',
    week: parts[5] || '?',
    year: parts[6] || undefined
  }
})

const cronObject = computed(() => ({
  second: secondExpression.value,
  minute: minuteExpression.value,
  hour: hourExpression.value,
  day: dayExpression.value,
  month: monthExpression.value,
  week: weekExpression.value,
  year: yearExpression.value
}))

const secondExpression = ref(expressions.value.second)
const minuteExpression = ref(expressions.value.minute)
const hourExpression = ref(expressions.value.hour)
const dayExpression = ref(expressions.value.day)
const monthExpression = ref(expressions.value.month)
const weekExpression = ref(expressions.value.week)
const yearExpression = ref(expressions.value.year)

const tabs = [
  { label: t('cron.text.second'), value: 'seconds' },
  { label: t('cron.text.minute'), value: 'minutes' },
  { label: t('cron.text.hour'), value: 'hours' },
  { label: t('cron.text.day'), value: 'day' },
  { label: t('cron.text.month'), value: 'month' },
  { label: t('cron.text.week'), value: 'week' },
  { label: t('cron.text.year'), value: 'year' }
]

const handleUpdate = (type: string, value: string) => {
  switch (type) {
    case 'second':
      secondExpression.value = value
      break
    case 'minute':
      minuteExpression.value = value
      break
    case 'hour':
      hourExpression.value = value
      break
    case 'day':
      dayExpression.value = value
      if (value !== '?' && weekExpression.value !== '?') {
        weekExpression.value = '?'
      }
      break
    case 'month':
      monthExpression.value = value
      break
    case 'week':
      weekExpression.value = value
      if (value !== '?' && dayExpression.value !== '?') {
        dayExpression.value = '?'
      }
      break
    case 'year':
      yearExpression.value = value
      break
  }
}

const calculateNextExecutionTime = (cronExpression?: string) => {
  if (cronExpression) {
    nextExecutionTimes.value = getNextExecutionTimes(cronExpression, 5)
  }
  else {
    nextExecutionTimes.value = []
  }
}

function getNextExecutionTimes(cronExpression: string, count: number = 5): string[]
{
  try {
    return getRecentTriggerTime(cronExpression, count)
  }
  catch (err) {
    console.error(err)
    return []
  }
}

onMounted(() => {
  const initialValue = localModelValue.value || '* * * * * ?'
  calculateNextExecutionTime(initialValue)
})

const handleClickOutside = (event: Event) => {
  if (
      inputRef.value &&
      panelRef.value &&
      !inputRef.value.contains(event.target as Node) &&
      !panelRef.value.contains(event.target as Node)
  ) {
    isOpen.value = false
  }
}

onMounted(() => {
  document.addEventListener('click', handleClickOutside)
})

onUnmounted(() => {
  document.removeEventListener('click', handleClickOutside)
})

watch([secondExpression, minuteExpression, hourExpression, dayExpression, monthExpression, weekExpression, yearExpression], () => {
  const parts = [
    secondExpression.value || '*',
    minuteExpression.value || '*',
    hourExpression.value || '*',
    dayExpression.value || '*',
    monthExpression.value || '*',
    weekExpression.value || '?'
  ]

  if (yearExpression.value !== undefined) {
    parts.push(yearExpression.value)
  }

  const newValue = parts.join(' ')
  localModelValue.value = newValue
  calculateNextExecutionTime(newValue)
  emit('on-change', newValue)
  emit('update:modelValue', newValue)
})

watch(activeTab, () => {
  const currentValue = localModelValue.value || '* * * * * ?'
  calculateNextExecutionTime(currentValue)
})

watch(localModelValue, (newValue) => {
  try {
    const parts = (newValue || '* * * * * ?').trim().split(' ')
    if (parts.length >= 6) {
      secondExpression.value = parts[0] || '*'
      minuteExpression.value = parts[1] || '*'
      hourExpression.value = parts[2] || '*'
      dayExpression.value = parts[3] || '*'
      monthExpression.value = parts[4] || '*'
      weekExpression.value = parts[5] || '?'
      yearExpression.value = parts.length > 6 ? parts[6] : undefined
      calculateNextExecutionTime(newValue)
    }
    else {
      throw new Error('Invalid cron expression')
    }
  }
  catch (err) {
    nextExecutionTimes.value = []
  }
})
</script>